/* kernel.c
*/

/*
 *  RYMOS
 *  Copyright (C) 2003 - 2012 Roberto Rodriguez 
 *
 *  This program is free software; you can redistribute it and/or modify 
 *  it under the terms of the GNU General Public License as published by 
 *  the Free Software Foundation; either version 2 of the License, or 
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License 
 *  along with this program; if not, write to the Free Software 
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * contact me by e-mail<romyt01@yahoo.es><prfiredragon@gmail.com>
 * ICQ#: 154747154
 *
 */






#include "system.h"
#include "k_defines.h"
#include "video.h"
#include "line.h"
#include "char.h"
#include "windows.h"
#include "colors.h"
#include "mm.h"
#include <multiboot.h>
#include <elf.h>
#include <pmm.h>
#include <vmm.h>
#include <heap.h>
#include <task.h>

//unsigned int panic(char *message);
unsigned int k_printf(char *message, unsigned int line);

unsigned short *textmemptr;
int attrib = 0x0F;
int csr_x = 0, csr_y = 0;

unsigned long Total_MB_Memory;
unsigned int Total_KB_Memory;
unsigned int Non_Use_Memory;

unsigned char boot_device;
unsigned char boot_partition;

elf_t kernel_elf;


void enable_ints()
{
	asm("sti");
};
void disable_ints()
{
	asm("cli");
};

int fn(void *arg)
{
  for(;;) {
    puts("a");
	
  }
  return 6;
}
int fn2(void *arg)
{
int i;
  for(i=0;i<20;i++) {
    puts("c");
  }
  return 6;
}
void terrmsgbox(char* cap,char* msg,int x,int y);
int msg_test(void *arg)
{
int i;
for(i=0;i<10;i++)
	terrmsgbox("INFORMATION$","WELCOME TO RYMOS Ver 0.0.0.1$",i,i+10);
return 6;
}

int msg_testb(void *arg)
{
int i;
for(i=200;i<210;i++)
	terrmsgbox("INFORMATION$","WELCOME TO RYMOS Ver 0.0.0.1$",i,i+10);
return 6;
}

int k_main( multiboot_info_t* mbd, unsigned int magic ) // like main
{
	init_video();
	ChkMagic(magic);
	printf("Welcome to RYMOS.\n");
	CalcMem(mbd);
	ChkBootDisk(mbd);
	gdt_install();
	printf("GDT installed.\n");
	idt_install();
	printf("IDT installed.\n");
	keyboard_install();
	printf("Keyboard installed.\n");
	//timer_install(100);
	set_timer_freq(100);
	printf("Timer installed.\n");

	MmapZise = mbd->mmap_addr + mbd->mmap_length;
	
	extern u32int end;
	u32int placement_address = (u32int)&end + (u32int)MmapZise;

/*	// Find the location of our initial ramdisk.
    	ASSERT(mbd->mods_count > 0);
    	u32int initrd_location = *((u32int*)mbd->mods_addr);
    	u32int initrd_end = *(u32int*)(mbd->mods_addr+4);
*/
	//panic ("Testing panic mechanism");
	init_pmm (placement_address);//mbd->mem_upper);
	init_vmm ();
	init_heap ();
	
	InitMem(mbd);



	kernel_elf = elf_from_multiboot (mbd);


	//asm volatile ("sti");
	enable_ints();
	init_tasking();

//	CreateTask("Test Task", fn);

/*  for(;;) {
    puts("b");
  }
*/
	
	Init_HW();



//----------------------------------------------------------------------------------------
//        setchartables();
//        init_windows();
//        box(0,0,640,480,green);
//----------------------------------------------------------------------------------------
        
	/*drawWindow(20,20,400,400,7);
        errmsgbox("TEST$","TEST MSGBOX$");
        */
//----------------------------------------------------------------------------------------
//	idt_set_gate(48, (unsigned)RdrawWindow, 0x08, 0x8E);
//        errmsgbox("INFORMATION$","WELCOME TO RYMOS Ver 0.0.0.1$");
	// errmsgbox(" $","WELCOME TO RYMOS Ver 0.0.0.1$");
//----------------------------------------------------------------------------------------        
//	CreateTask("Window Manager", winman);
//	CreateTask("Window Manager", msg_testb);
//	CreateTask("Window Manager", msg_test);


	return 0;
}



/* Scrolls the screen */
void Cscroll(void)
{
    unsigned blank, temp;

    /* A blank is defined as a space... we need to give it
    *  backcolor too */
    blank = 0x20 | (attrib << 8);

    /* Row 25 is the end, this means we need to scroll up */
    if(csr_y >= 25)
    {
        /* Move the current text chunk that makes up the screen
        *  back in the buffer by a line */
        temp = csr_y - 25 + 1;
        memcpy (textmemptr, textmemptr + temp * 80, (25 - temp) * 80 * 2);

        /* Finally, we set the chunk of memory that occupies
        *  the last line of text to our 'blank' character */
        memsetw (textmemptr + (25 - temp) * 80, blank, 80);
        csr_y = 25 - 1;
    }
}

/* Updates the hardware cursor: the little blinking line
*  on the screen under the last character pressed! */
void move_csr(void)
{
    unsigned temp;

    /* The equation for finding the index in a linear
    *  chunk of memory can be represented by:
    *  Index = [(y * width) + x] */
    temp = csr_y * 80 + csr_x;

    /* This sends a command to indicies 14 and 15 in the
    *  CRT Control Register of the VGA controller. These
    *  are the high and low bytes of the index that show
    *  where the hardware cursor is to be 'blinking'. To
    *  learn more, you should look up some VGA specific
    *  programming documents. A great start to graphics:
    *  http://www.brackeen.com/home/vga */
    outportb(0x3D4, 14);
    outportb(0x3D5, temp >> 8);
    outportb(0x3D4, 15);
    outportb(0x3D5, temp);
}

/* Clears the screen */
void cls()
{
    unsigned blank;
    int i;

    /* Again, we need the 'short' that will be used to
    *  represent a space with color */
    blank = 0x20 | (attrib << 8);

    /* Sets the entire screen to spaces in our current
    *  color */
    for(i = 0; i < 25; i++)
        memsetw (textmemptr + i * 80, blank, 80);

    /* Update out virtual cursor, and then move the
    *  hardware cursor */
    csr_x = 0;
    csr_y = 0;
    move_csr();
}

/* Puts a single character on the screen */
void putch(unsigned char c)
{
    unsigned short *where;
    unsigned att = attrib << 8;

    /* Handle a backspace, by moving the cursor back one space */
    if(c == 0x08)
    {
        if(csr_x != 0) csr_x--;
    }
    /* Handles a tab by incrementing the cursor's x, but only
    *  to a point that will make it divisible by 8 */
    else if(c == 0x09)
    {
        csr_x = (csr_x + 8) & ~(8 - 1);
    }
    /* Handles a 'Carriage Return', which simply brings the
    *  cursor back to the margin */
    else if(c == '\r')
    {
        csr_x = 0;
    }
    /* We handle our newlines the way DOS and the BIOS do: we
    *  treat it as if a 'CR' was also there, so we bring the
    *  cursor to the margin and we increment the 'y' value */
    else if(c == '\n')
    {
        csr_x = 0;
        csr_y++;
    }
    /* Any character greater than and including a space, is a
    *  printable character. The equation for finding the index
    *  in a linear chunk of memory can be represented by:
    *  Index = [(y * width) + x] */
    else if(c >= ' ')
    {
        where = textmemptr + (csr_y * 80 + csr_x);
        *where = c | att;	/* Character AND attributes: color */
        csr_x++;
    }

    /* If the cursor has reached the edge of the screen's width, we
    *  insert a new line in there */
    if(csr_x >= 80)
    {
        csr_x = 0;
        csr_y++;
    }

    /* scroll the screen if needed, and finally move the cursor */
    Cscroll();
    move_csr();
}

/* Sets the forecolor and backcolor that we will use */
void settextcolor(unsigned char forecolor, unsigned char backcolor)
{
    /* Top 4 bytes are the background, bottom 4 bytes
    *  are the foreground color */
    attrib = (backcolor << 4) | (forecolor & 0x0F);
}

/* Sets our text-mode VGA pointer, then clears the screen for us */
void init_video(void)
{
    textmemptr = (unsigned short *)0xB8000;
    cls();
}
	
unsigned short *memsetw(unsigned short *dest, unsigned short val, size_t count)
{
    unsigned short *temp = (unsigned short *)dest;
    for( ; count != 0; count--) *temp++ = val;
    return dest;
}

char* itoa(int val, int base){
	
	static char buf[32] = {0};
	
	int i = 30;
	
	for(; val && i ; --i, val /= base)
	
		buf[i] = "0123456789abcdef"[val % base];
	
	return &buf[i+1];
	
}
/*
void monitor_write_hex(u32int n)
{
    s32int tmp;

    printf("0x");

    char noZeroes = 1;

    int i;
    for (i = 28; i > 0; i -= 4)
    {
        tmp = (n >> i) & 0xF;
        if (tmp == 0 && noZeroes != 0)
        {
            continue;
        }
    
        if (tmp >= 0xA)
        {
            noZeroes = 0;
            printf(itoa(tmp-0xA+'a',12));
        }
        else
        {
            noZeroes = 0;
            printf( itoa(tmp+'0',12) );
        }
    }
  
    tmp = n & 0xF;
    if (tmp >= 0xA)
    {
        printf(itoa(tmp-0xA+'a',12));
    }
    else
    {
        printf(itoa(tmp+'0',12));
    }

}
*/
extern void panic_assert(const char *file, u32int line, const char *desc)
{
    // An assertion failed, and we have to panic.
    asm volatile("cli"); // Disable interrupts.

    printf("ASSERTION-FAILED(");
    printf(desc);
    printf(") at ");
    printf(file);
    printf(":");
    printf("%d",line);
    printf("\n");
    // Halt by going into an infinite loop.
    for(;;);
}

int CalcMem(multiboot_info_t* mbd)
{

	if(mbd->flags & 0x01)//check that bit zero is set
	{
	unsigned long total_memory_size = ((unsigned long)mbd->mem_lower + (unsigned long)mbd->mem_upper);
	Total_Alloc_Memory = ((total_memory_size*1024));
	Total_KB_Memory = mbd->mem_upper + mbd->mem_lower;
	if (Total_Alloc_Memory >= 6442450944) 
	{
		Total_Alloc_Memory = 6442450944;
		Total_MB_Memory = ((Total_Alloc_Memory/1024)/1024);
		Total_KB_Memory = (Total_Alloc_Memory*1024);
		
	}
	else
	{
		Total_MB_Memory = (total_memory_size/1024);
	}
	printf("Total memory detected %d MB\n", Total_MB_Memory);
	
	}
	else
	{
	
		puts("Warning: Memory size information not avaliable.\nAssuming that you have 16MB of memory.\n");
		Total_KB_Memory = 16777216;//16MB
		Total_Alloc_Memory = (Total_KB_Memory*1024);
		Total_MB_Memory = ((Total_Alloc_Memory/1024)/1024);				
	}
}

int ChkMagic(unsigned int magic)
{
	if ( magic != 0x2BADB002 )
	{
	 /* Something went not according to specs. Print an error */
	 /* message and halt, but do *not* rely on the multiboot */
	 /* data structure. */
	printf("Something went not according to specs.\n");
	for (;;);
	}
}

int ChkBootDisk(multiboot_info_t* mbd)
{
	if(mbd->flags & 0x02)//check that bit one is set
	{
		boot_device = (unsigned char)(mbd->boot_device >> 24);
		printf("Boot device %d\n", boot_device);
		if(boot_device & 0x80)
		{
			printf("Boot device /devices/hda0\n");
		}
	}
	else
	{
		printf("Warning: Boot device information not avaliable. Setting the Default to the floppy drive.\n");
		
		boot_device = 0;//set default to 0 or floppy drive.			
	}
}

int InitMem(multiboot_info_t* mbd)
{
	// Find all the usable areas of memory and inform the physical memory manager about them.
  uint32_t i = mbd->mmap_addr;
  while (i < Total_Alloc_Memory)//mbd->mmap_addr + mbd->mmap_length)
  {
    multiboot_memory_map_t *me = (multiboot_memory_map_t*) i;

    // Does this entry specify usable RAM?
    if (me->type == 1)
    {
      uint32_t j;
      // For every page in this entry, add to the free page stack.
      for (j = me->addr; j < me->addr+me->len; j += 0x1000)
      {
        pmm_free_page (j);
      }
    }
    // The multiboot specification is strange in this respect - the size member does not include "size" itself in its calculations,
    // so we must add sizeof (uint32_t).
    i += me->size + sizeof (uint32_t);
  }
}
